package com.loong.socket;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import androidx.annotation.NonNull;

import com.alibaba.fastjson.JSONObject;
import com.loong.socket.handler.SocketUiHandler;
import com.loong.socket.net.TcpClient;

import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.bridge.UniJSCallback;
import io.dcloud.feature.uniapp.common.UniModule;

import static com.loong.socket.consts.Consts.*;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class FileSyncModule extends UniModule implements Handler.Callback {

    private static final String TAG = "FileSync";

    private final SocketUiHandler mSocketUiHandler = new SocketUiHandler(this);

    private UniJSCallback mCallback;

    private final StringBuilder result = new StringBuilder();

    private boolean isDebug = false;

    //run ui thread
    @UniJSMethod(uiThread = true)
    public void testAsyncFunc(JSONObject options, UniJSCallback callback) {
        Log.e(TAG, "testAsyncFunc--" + options);
        if (callback != null) {
            JSONObject data = new JSONObject();
            data.put("code", "success");
            callback.invoke(data);
            //callback.invokeAndKeepAlive(data);
        }
    }

    //run JS thread
    @UniJSMethod(uiThread = false)
    public void sendFile(String domain, int port, String filePath, UniJSCallback callback) {
        mCallback = callback;
        String fileName = findFileName(filePath);
        String usePath = checkFile(filePath, fileName);
        if (usePath != null) {
            startSocketWithThread(usePath, domain, port);
        } else {
            result.append("file not exists.");
            resolveResult(-2, result.toString());
        }
    }

    private String findFileName(String filePath) {
        return filePath.substring(filePath.lastIndexOf("/") + 1);
    }


    private void startSocketWithThread(String filePath, String domain, int port) {
        TcpClient tcpClient = new TcpClient(filePath, domain, port, mSocketUiHandler);
        //tcpClient.setDataToSend(FileUtils.fileToBytes(mFilePath));
        tcpClient.start();

    }

    private void recordLog(String message) {
        result.append(message).append("\n");
    }

    private String checkFile(String filePath, String name) {
        if (isDebug) {
            recordLog("filePath: " + filePath + "\nfileName：" + name + "\n");
        }

        // 统一路径处理逻辑
        File fileToCheck;

        List<File> files = findTargetFiles(name);
        if (isDebug) {
            recordLog("\nExternal: \n");
        }
        for (File file : files) {
            try {
                String canonicalPath = file.getCanonicalPath();
                if (isDebug) {
                    recordLog(canonicalPath + "\n");
                }
            } catch (Exception e) {
                Log.w(TAG, "Cannot access file: " + file.getAbsolutePath(), e);
            }
        }

        if (files.isEmpty()) {
            fileToCheck = new File(filePath);
        } else {
            fileToCheck = files.get(0);
        }

        // 确保文件存在且是文件（不是目录）
        if (fileToCheck.exists() && fileToCheck.isFile()) {
            try {
                // 返回规范化路径
                return fileToCheck.getCanonicalPath();
            } catch (Exception e) {
                Log.e(TAG, "Failed to get canonical path", e);
                return null;
            }
        }

        return null;
    }

    private List<File> findTargetFiles(String name) {

        if (mUniSDKInstance != null && mUniSDKInstance.getContext() != null) {
            // 在应用上下文中，基于应用数据目录解析路径
            Context context = mUniSDKInstance.getContext();
            File filesDir = context.getExternalFilesDir(null);
            if (filesDir != null) {
                File rootDir = filesDir.getParentFile();
                return findAllFilesInDirectory(rootDir, name);
            }
        }

        return new ArrayList<>();
    }


    /**
     * 在目录及其子目录中递归查找所有包含指定路径的文件
     *
     * @param directory 要搜索的目录
     * @param path      要匹配的文件路径片段
     * @return 包含所有匹配文件的列表
     */
    private List<File> findAllFilesInDirectory(File directory, String path) {
        List<File> result = new ArrayList<>();

        // 检查输入参数
        if (directory == null || !directory.exists() || !directory.isDirectory()) {
            return result;
        }

        if (path == null || path.isEmpty()) {
            return result;
        }

        findAllFilesRecursive(directory, path, result);
        return result;
    }

    /**
     * 递归查找文件的辅助方法
     *
     * @param directory 当前搜索的目录
     * @param path      要匹配的文件路径片段
     * @param result    结果列表
     */
    private void findAllFilesRecursive(File directory, String path, List<File> result) {
        // 获取目录中的所有文件和子目录
        File[] files = directory.listFiles();
        if (files == null) {
            return;
        }

        // 遍历所有文件和目录
        for (File file : files) {
            try {
                // 获取文件的规范路径
                String canonicalPath = file.getCanonicalPath();

                // 检查路径是否包含指定的path
                if (canonicalPath.contains(path)) {
                    result.add(file);
                }

                // 如果是目录，则递归搜索
                if (file.isDirectory()) {
                    findAllFilesRecursive(file, path, result);
                }
            } catch (Exception e) {
                // 忽略无法访问的文件或目录
                Log.w(TAG, "Cannot access file: " + file.getAbsolutePath(), e);
            }
        }
    }


    private static final String STR_CONNECT_BEGIN = "connecting server...";
    private static final String STR_CONNECT_SUCESS = "connect success.";
    private static final String STR_CONNECT_FAIL = "connect fail!";
    private static final String STR_SEND_BEGIN = "sending data...";
    private static final String STR_SEND_FINISH = "send finish.";
    private static final String STR_CONNECT_CLOSE = "send success. connect close.";
    private static final String STR_ACK_ERROR = "receive ACK from server error.";


    @Override
    public boolean handleMessage(@NonNull Message msg) {
        boolean isHandled = false;
        switch (msg.what) {
            case MSG_CONNECT_BEGIN:
                isHandled = true;
                //mTvSocketTips.setText(STR_CONNECT_BEGIN);
                resolveResult(0, STR_CONNECT_BEGIN);
                break;

            case MSG_CONNECT_SUCCESS:
                isHandled = true;
                //mTvSocketTips.setText(STR_CONNECT_SUCESS);
                resolveResult(1, STR_CONNECT_SUCESS);
                break;

            case MSG_CONNECT_FAIL:
                isHandled = true;
                String message = null;
                if (msg.obj instanceof String) {
                    message = msg.obj.toString();
                }
                String target = message != null ? message : STR_CONNECT_FAIL;
                //mTvSocketTips.setText(STR_CONNECT_FAIL);
                resolveResult(-1, target);
                break;

            case MSG_SEND_BEGIN:
                isHandled = true;
                //mTvSocketTips.setText(STR_SEND_BEGIN);
                resolveResult(10, STR_SEND_BEGIN);
                break;

            case MSG_SEND_FINISH:
                isHandled = true;
                //mTvSocketTips.setText(STR_SEND_FINISH);
                resolveResult(11, STR_SEND_FINISH);
                break;

            case MSG_CONNECT_CLOSE:
                isHandled = true;
                //mTvSocketTips.setText(STR_CONNECT_CLOSE);
                resolveResult(100, STR_CONNECT_CLOSE);
                break;

            default:
                isHandled = true;
                resolveResult(200, STR_CONNECT_CLOSE);
                break;
        }
        return isHandled;
    }


    private void resolveResult(int code, String message) {
        if (mCallback != null) {
            JSONObject data = new JSONObject();
            data.put("code", code);
            data.put("message", message);
            //callback.invoke(data);
            mCallback.invokeAndKeepAlive(data);
        }
    }

}
